﻿using System;
using System.Collections.Generic;
using Sharp3DMath.Core;
using Sharp3DMath.Geometry2D;
using Sharp3DMath.Strokes;
using System.Text;


namespace Sharp3DMath.Strokes
{

    /// <summary>
    /// The Points of CH,LT,LQ,ER;
    /// The Length of the Stroke;
    /// The Area and Perimeter of the Geometric Figures Sharp.
    /// </summary>
    public class GlobalGeometricFeatureGenerator
    {
        #region Private Attribute
        List<Vector2F> chPoints;
        double pch, ach;

        Vector2F[] ltPoints;
        double plt, alt;

        Vector2F[] lqPoints;
        double plq, alq;

        Vector2F[] erPoints;
        double per, aer, wer, her;

        double tl, hollowness;
        Vector2F[] stPoints;

        #endregion
        #region Public Attribute
        #region Geometric Figures
        public Vector2F[] ChPoints
        {
            get { return chPoints.ToArray(); }
        }
        public Vector2F[] LtPoints
        {
            get { return ltPoints; }
        }
        public Vector2F[] LqPoints
        {
            get { return lqPoints; }
        }
        public Vector2F[] ErPoints
        {
            get { return erPoints; }
        }
        #endregion
        #region Geometric Features
        public double Ach
        {
            get { return ach; }
        }
        public double Pch { get { return pch; } }
        public double Alt { get { return alt; } }
        public double Plt { get { return plt; } }
        public double Alq { get { return alq; } }
        public double Plq { get { return plq; } }
        public double Aer { get { return aer; } }
        public double Per { get { return per; } }
        public double Her { get { return her; } }
        public double Wer { get { return wer; } }
        public double Tl { get { return tl; } }
        public double Hollowness { get { return hollowness; } }
        #endregion
        #endregion
        public GlobalGeometricFeatureGenerator()
        {
            chPoints = new List<Vector2F>();
            ltPoints = new Vector2F[3];
            lqPoints = new Vector2F[4];
            erPoints = new Vector2F[4];
        }

        #region Constructor
        public GlobalGeometricFeatureGenerator(Stroke stroke)
            : this()
        {
            tl = stroke.StrokeLength;
            Vector2F[] chpts = MathEx.ConvexHull(stroke);
            SetConvexHull(chpts);
            SetEnclosingRectangle(chpts);
            SetLargestTriangle(chpts);
            SetLargestQuadrilateral(chpts);
            hollowness = SetHollowness(stroke);
        }
        /// <summary>
        /// 暂时不可用
        /// </summary>
        /// <param name="pts"></param>
        public GlobalGeometricFeatureGenerator(Vector2F[] pts)
            : this()
        {
            tl = StrokeAnalyzer.GetStrokeLength(pts);
            Vector2F[] chpts = MathEx.ConvexHull(pts);
            SetConvexHull(chpts);
            SetEnclosingRectangle(chpts);
            SetLargestTriangle(chpts);
            SetLargestQuadrilateral(chpts);
        }
        /// <summary>
        /// 暂时不可用
        /// </summary>
        /// <param name="pts"></param>
        public GlobalGeometricFeatureGenerator(List<Vector2F> pts)
        {
            tl = StrokeAnalyzer.GetStrokeLength(pts);
            Vector2F[] chpts = MathEx.ConvexHull(pts);
            SetConvexHull(chpts);
            SetEnclosingRectangle(chpts);
            SetLargestTriangle(chpts);
            SetLargestQuadrilateral(chpts);
        }

        #endregion
        #region Set GeometricFigures
        protected void SetConvexHull(Vector2F[] chpts)
        {
            chPoints.Clear();
            if (chpts.Length <= 0)
            {
                pch = 0;
                ach = 0;
                return;
            }
            for (int i = 0; i < chpts.Length - 1; i++)
            {
                chPoints.Add(chpts[i]);
                pch += DistanceMethods.Distance(chpts[i], chpts[i + 1]);
            }
            chPoints.Add(chpts[chpts.Length - 1]);
            pch += DistanceMethods.Distance(chpts[chpts.Length - 1], chpts[0]);
            ach = MathEx.AreaConvexHull(chPoints);

        }
        protected void SetEnclosingRectangle(Vector2F[] chpts)
        {
            if (chpts.Length <= 0)
            {
                per = 0;
                aer = 0;
                her = 0;
                wer = 0;
                erPoints.Initialize();
                return;
            }
            aer = MathEx.MinAreaEnclosingRectangle(chpts, ref erPoints, ref her, ref wer);
            per = her + wer + her + wer;
        }
        protected void SetLargestTriangle(Vector2F[] chpts)
        {
            if (chpts.Length < 3)
            {
                alt = 0;
                plt = 0;
                ltPoints.Initialize();
                return;
            }
            alt = MathEx.AltConvexHull(chpts, ref ltPoints);
            plt = 0;
            plt = DistanceMethods.Distance(ltPoints[0], ltPoints[1]);
            plt += DistanceMethods.Distance(ltPoints[1], ltPoints[2]);
            plt += DistanceMethods.Distance(ltPoints[2], ltPoints[1]);
        }
        protected void SetLargestQuadrilateral(Vector2F[] chpts)
        {
            if (chpts.Length < 4)
            {
                alq = 0;
                plq = 0;
                lqPoints.Initialize();
                return;
            }
            alq = MathEx.AlqConvexHull(chpts, ref lqPoints);
            plq = DistanceMethods.Distance(lqPoints[0], lqPoints[1]);
            plq += DistanceMethods.Distance(lqPoints[1], lqPoints[2]);
            plq += DistanceMethods.Distance(lqPoints[2], lqPoints[3]);
            plq += DistanceMethods.Distance(lqPoints[0], lqPoints[3]);
        }
        //用简单的
        protected double SetHollowness(Stroke stroke)
        {//Todo Hollowness
            Vector2F center = StrokeAnalyzer.StrokeCenter(stroke);
            List<Segment> lines = new List<Segment>(chPoints.Count);
            double sum = 0; int intersectcount = 0;
            foreach (var item in chPoints)
            {
                lines.Add(new Segment(center, item));
            }


            for (int i = 0; i < stroke.Points.Count-1; i++)
            {
                Segment line = new Segment(stroke.Points[i], stroke.Points[i + 1]);
                
                foreach (var item in lines)
                {
                    if (MathEx.LinesIntersect(line,item))
                    {
                        double dis = DistanceMethods.Distance(stroke.Points[i],item.P1)+DistanceMethods.Distance(stroke.Points[i+1],item.P1);
                        sum += dis / item.Length;
                        intersectcount++;        
                        break;
                    }
                }
            }
            if (intersectcount==0)
            {
                return 0;
            }
            else
            {
                return sum * intersectcount/chPoints.Count;
            }
            
        }
        #endregion

        public GlobalGeometricFeature LoadFrom(Stroke stroke)
        {
            Clear();
            tl = stroke.StrokeLength;
            Vector2F[] chpts = MathEx.ConvexHull(stroke);
            SetConvexHull(chpts);
            SetEnclosingRectangle(chpts);
            SetLargestTriangle(chpts);
            SetLargestQuadrilateral(chpts);
            hollowness = SetHollowness(stroke);//在SetConvexHull后调用
            GlobalGeometricFeature feature = GlobalGeometricFeature.Zero;
            #region
            feature.Ach = Ach;
            feature.Aer = Aer;
            feature.Alq = Alq;
            feature.Alt = Alt;
            feature.Her = Her;
            feature.Hollowness = Hollowness;
            feature.Pch = Pch;
            feature.Per = Per;
            feature.Plq = Plq;
            feature.Plt = Plt;
            feature.Tl = Tl;
            feature.Wer = Wer;
            #endregion
            return feature;
        }
        public void Clear()
        {
            chPoints.Clear();
            for (int i = 0; i < 3; i++)
            {
                ltPoints[i].X = 0;
                ltPoints[i].Y = 0;

                lqPoints[i].X = 0;
                lqPoints[i].Y = 0;

                erPoints[i].X = 0;
                erPoints[i].Y = 0;
            }

            erPoints[3].X = 0;
            erPoints[3].Y = 0;
            lqPoints[3].X = 0;
            lqPoints[3].Y = 0;
            hollowness=pch = ach = alt = plt = alq = plq = aer = per = her = wer = tl = 0;
        }
    }




}
